/**
 * https://blog.csdn.net/duanhy_love/article/details/115768516
 * 1.闭包和高阶函数
 * 2.作用域：只有函数作用域，全局作用域，没有块作用域。特殊情况除外（后面会说到 try cache/let/const）
 * 3.函数声明和函数表达式
 */



/** 函数作用域 */
  var a = 1;
  function foo() {
    var a = 2;
    console.log(a); // 2 当前作用域
  }
  foo(); // foo执行后出栈，就像这个函数从来不存在一样
  console.log(a); // 1 全局作用域

  /** 上面这个好像能理解，继续往下看 */
  /**
   * 上面存在的问题：
   * 1.要额外创建 foo 函数
   * 2.污染当前作用域（这段代码之前或之后存在其他 foo 变量，可能导致异常）
   * 3.必须显式调用 foo 函数
   */

   var a1 = 1;
   (function foo1() {
     var a1 = 2;
     console.log('我来自内部', foo1); // 我来自内部 ƒ foo() {...}
     console.log(a1); // 2 局部变量
   })();
   console.log(a1); // 1 全局变量
   console.log(foo1); // ReferenceError: foo1 is not defined foo 去哪了???


   /** 是不是有点理解不了？先了解一下函数声明和函数表达式的区别，查看about-function.js文件 */

   /**
    * IIFE（立即调用函数表达式）
    * (function () {
    *    statements
    * })();
   */

   /** ----------------------- 开始讨论块作用域----------------------- */

   // 同步执行 依次输出0-9；很好理解
  console.log('变量提升 ', i); // 变量提升 undefined
  for (var i = 0; i < 10; i++) {
    console.log(i);
  }
  console.log('循环执行完毕 全局变量 ', i); // 循环执行完毕 全局变量 10

  // 异步执行 输出10次10；也能理解
for (var i = 0; i < 10; i++) {
  setTimeout(() => {
    // console执行时 for循环已经执行完毕 此时i=10
    console.log(i);
  }, 0);
}
console.log('循环执行完毕 全局变量 ', i); // 循环执行完毕 全局变量 10

/** ------------------------ JavaScript 实现块作用域 ------------------------ */
// 1.try/catch
  try {
    undefined(); // 执行一个非法操作来强制制造一个异常
  } catch (err) {
    console.log(err); // 能够正常执行！
  }
  console.log(err); // ReferenceError: err not found

// 2.let/const
// 异步执行 依次输出0-9
for (let i = 0; i < 10; i++) {
  setTimeout(() => {
    console.log(i);
  }, 0);
}
/** 循环内部使用 let 创建了块级作用域，变量 i 查找时，使用块级作用域内的值。 因为 setTimeout 内部函数引用了变量i，循环执行完毕，定时器执行前，变量 i 还没有被回收。 */

// 3.IIFE 立即执行函数（模拟块作用域）

// 异步执行 依次输出0-9 （利用闭包改造）
for (var i = 0; i < 10; i++) {
  (function(j) {
    setTimeout(() => {
      // console执行时 j取的是当前作用域中的值 所以输出0-9
      console.log(j);
    }, 0);
  })(i);
}

/** ------------------------ JavaScript 块作用域讨论结束 ------------------------ */

/** 闭包 */

/** 高阶函数
 * 是指使用其他函数作为参数、或者返回一个函数作为结果的函数
 * 函数作为参数：常用的数组方法，比如 map/reduce/filter/sort 等。
 */

/**
 * 高阶函数应用：
 * 1.柯里化与反柯里化
 */

 (() => {
  for(let i = 0;i<10;i++) {
      setTimeout(() => {
          console.info(i)
      }, 0)
  }
})();(() => {
  for(let i = 0;i<10;i++) {
      setTimeout(() => {
          console.info(i)
      }, 0)
  }
})();